#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>

#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <evhttp.h>

#include "ac_bm.h"

#define MAX_LINE 16384
pattern_tree * ptree;
pattern_data * pdata;
void do_read(evutil_socket_t fd, short events, void *arg);
void do_write(evutil_socket_t fd, short events, void *arg);

int daemonize(int nochdir, int noclose)
{
    int fd;

    switch (fork()) {
        case -1:
            return (-1);
        case 0:
            break;
        default:
            _exit(EXIT_SUCCESS);
    }

    if (setsid() == -1)
        return (-1);

    if (nochdir == 0) {
        if(chdir("/") != 0) {
            perror("chdir");
            return (-1);
        }
    }

    if (noclose == 0 && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
        if(dup2(fd, STDIN_FILENO) < 0) {
            perror("dup2 stdin");
            return (-1);
        }
        if(dup2(fd, STDOUT_FILENO) < 0) {
            perror("dup2 stdout");
            return (-1);
        }
        if(dup2(fd, STDERR_FILENO) < 0) {
            perror("dup2 stderr");
            return (-1);
        }

        if (fd > STDERR_FILENO) {
            if(close(fd) < 0) {
                perror("close");
                return (-1);
            }
        }
    }
    return (0);
}


void tree_init(const char* data_file_dir)
{
    FILE *fp;
    int data_count = 0;
    char *line = (char *)malloc(sizeof(char)*4096);


    fp = fopen(data_file_dir,"r");
    if(!fp) {
        perror("Read data file error\n");
        exit(1);
    }


    while(!feof(fp)) {
        fgets(line,4096,fp);
        if(strlen(line) > 0) {
            data_count ++;
        }
        strcpy(line,"");
    }
    if(data_count == 0) {
        perror("Data file is empty");
        fclose(fp);
        exit(1);
    }

    fseek(fp,0,SEEK_SET);
    pdata = (pattern_data*)malloc(sizeof(pattern_data)*data_count);

    int i=0;
    while(!feof(fp)) {
        fgets(line,4096,fp);
        if(strlen(line) > 0) {
            line[strlen(line)-1] = '\0';
            pdata[i].len = strlen(line);
            strcpy(pdata[i].data,line);
            i++;
        }
        strcpy(line,"");
    }
    fclose(fp);
    free(line);
    ptree = acbm_init(pdata,data_count);
/*    free(pdata);*/
    fprintf(stdout,"AC tree initialize finish\n");
}
 
void readcb(struct bufferevent *bev, void *ctx)
{
    struct evbuffer *input, *output;
    char *line;
    size_t n;
    int i = 0;
    int nmax_index = 32;

    unsigned int matched_indexs[32] = {0,};
    int res_count = 0;


    input = bufferevent_get_input(bev);
    output = bufferevent_get_output(bev);

    while ((line = evbuffer_readln(input, &n, EVBUFFER_EOL_LF))) {
        strcat(line,"\0");
        res_count = acbm_search(ptree,line, strlen(line),matched_indexs,nmax_index);
        if(res_count) {
            for(i=0;i<res_count;i++) {
                evbuffer_add(output, pdata[matched_indexs[i]].data, strlen(pdata[matched_indexs[i]].data)); 
                evbuffer_add(output, " ", 1);
            }
            evbuffer_add(output, "\n", 1);
        }
        free(line);
    }

    if (evbuffer_get_length(input) >= MAX_LINE) { 
        char buf[1024];
        while (evbuffer_get_length(input)) {
            int n = evbuffer_remove(input, buf, sizeof(buf)-1); 
            strcat(buf,"\0");
            res_count = acbm_search(ptree,line, strlen(buf),matched_indexs,nmax_index);
            if(res_count) {
                for(i=0;i<res_count;i++) {
                    evbuffer_add(output, pdata[matched_indexs[i]].data, strlen(pdata[matched_indexs[i]].data)); 
                    evbuffer_add(output, " ", 1);
                }
            }
        }
        evbuffer_add(output, "\n", 1);
    }
}

void errorcb(struct bufferevent *bev, short error, void *ctx)
{
    if (error & BEV_EVENT_EOF) {
        /* connection has been closed, do any clean up here */
        /* ... */
    } else if (error & BEV_EVENT_ERROR) {
        /* check errno to see what error occurred */
        /* ... */
    } else if (error & BEV_EVENT_TIMEOUT) {
        /* must be a timeout event handle, handle it */
        /* ... */
    }
    bufferevent_free(bev);
}

void do_accept(evutil_socket_t listener, short event, void *arg)//监听sokect回调函数
{
    struct event_base *base = arg;
    struct sockaddr_storage ss;
    socklen_t slen = sizeof(ss);
    int fd = accept(listener, (struct sockaddr*)&ss, &slen);
    if (fd < 0) {
        perror("accept");
    } else if (fd > FD_SETSIZE) {
        close(fd);
    } else {
        struct bufferevent *bev; //进行读写工作
        evutil_make_socket_nonblocking(fd);
        bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
        bufferevent_setcb(bev, readcb, NULL, errorcb, NULL);
        bufferevent_setwatermark(bev, EV_READ, 0, MAX_LINE);
        bufferevent_enable(bev, EV_READ|EV_WRITE);
    }
}

void usage()
{
    fprintf(stdout,"Syntax:\n", stderr);
    fprintf(stdout,"   mmgrep -f <filename> -h <listen-on-addr>  -p <listen-on-port> [-d]\n", stderr);
    fprintf(stdout,"Example:\n", stderr);
    fprintf(stdout,"   mmgrep -f xxx.txt -h127.0.0.1 -p8888 -d\n", stderr);

    exit(1);
}

int main(int argc, char **argv)
{
    int c;
    char *file_name;
    char *host;
    int  port;
    int do_daemonize = 0;

    evutil_socket_t listener;
    struct sockaddr_in sin;
    struct event_base *base;
    struct event *listener_event;

    /* set stderr non-buffering (for running under, say, daemontools) */
    setbuf(stderr, NULL);

    while (-1 != (c = getopt(argc, argv,
                    "f:"  /* Data file dir */
                    "h:"  /* Listen host */
                    "p:"  /* TCP port number to listen on */
                    "d"
                    ))) {
        switch (c) {
            case 'f':
                file_name = optarg;
                fprintf(stdout," filename %s\n",file_name);
                break;
            case 'h':
                host = optarg;
                fprintf(stdout," host %s\n",host);
                break;
            case 'p':
                port = atoi(optarg);
                fprintf(stdout," port %d\n",port);
                break;
            case 'd':
                do_daemonize = 1;
                fprintf(stdout," deamon %d\n",do_daemonize);
                break;
            default:
                fprintf(stderr,"wrong input params");
                return (0);
        } 
    } 

    if(file_name == NULL || host == NULL || port <0 || port > 65535) {
        usage();
        return(0);
    }

    if (do_daemonize) {
        if (daemonize(4, 0) == -1) {
            fprintf(stderr, "failed to daemon() in order to daemonize\n");
            exit(EXIT_FAILURE);
        }
    }

    tree_init(file_name);
    base = event_base_new();

    if (!base)
        return;

    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    /*    sin.sin_addr.s_addr = INADDR_ANY;*/
    sin.sin_addr.s_addr = inet_addr(host);

    listener = socket(AF_INET, SOCK_STREAM, 0 ); 
    evutil_make_socket_nonblocking(listener); 

    int one = 1;
    setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));

    if (-1 == bind(listener, (struct sockaddr*)&sin, sizeof(sin)) ) { 
        perror("bind");
        return;
    }

    if (listen(listener, 128)<0) {
        perror("listen");
        return;
    }

    listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base); 
    event_add(listener_event, NULL);

    event_base_dispatch(base); 

    acbm_clean(ptree);
    free(pdata);
    event_free(listener_event);
    event_base_free(base);

    return 1;
}

